﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using SmartHotel.Services.FacilityManagement.Models;
using SmartHotel.Services.FacilityManagement.Swashbuckle;
using Swashbuckle.AspNetCore.Swagger;

namespace SmartHotel.Services.FacilityManagement
{
	public class Startup
	{
		public Startup( IConfiguration configuration )
		{
			Configuration = configuration;
		}

		public IConfiguration Configuration { get; }

		// This method gets called by the runtime. Use this method to add services to the container.
		public void ConfigureServices( IServiceCollection services )
		{
			services.Configure<AzureAdOptions>( options => Configuration.Bind( "AzureAd", options ) );
			services.Configure<BasicAuthOptions>( options => Configuration.Bind( "BasicAuth", options ) );
			ServiceProvider serviceProvider = services.BuildServiceProvider();
			AzureAdOptions azureAdOptions = serviceProvider.GetRequiredService<IOptions<AzureAdOptions>>().Value;

			BasicAuthOptions basicAuthOptions = serviceProvider.GetRequiredService<IOptions<BasicAuthOptions>>().Value;
			var authFlow = new AuthFlow();
			authFlow.BasicAuthOptions = basicAuthOptions;
			services.AddSingleton( typeof( AuthFlow ), authFlow );

			if ( authFlow.UseAdalAuthFlow )
			{
				services.AddAuthentication( JwtBearerDefaults.AuthenticationScheme )
					.AddJwtBearer( options =>
					 {
						 options.Audience = azureAdOptions.ApplicationId;
						 options.Authority = azureAdOptions.PassThroughAuthority;
						 options.TokenValidationParameters = new TokenValidationParameters
						 {
							 ValidateIssuer = false
						 };
					 } );
			}

			services.AddMvc( config =>
				 {
					 if ( authFlow.UseAdalAuthFlow )
					 {
						 // Adding ADAL authentication
						 var policy = new AuthorizationPolicyBuilder()
							  .RequireAuthenticatedUser()
							  .Build();
						 config.Filters.Add( new AuthorizeFilter( policy ) );
					 }
					 else
					 {
						 config.Filters.Add( new BasicFlowAuthorizationFilter( basicAuthOptions ) );
					 }
				 } ).SetCompatibilityVersion( CompatibilityVersion.Version_2_1 )
				.AddJsonOptions( o =>
				 {
					 o.SerializerSettings.DefaultValueHandling = Newtonsoft.Json.DefaultValueHandling.Ignore;
					 o.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
				 } );

			services.AddSwaggerGen( c =>
			 {
				 //The generated Swagger JSON file will have these properties.
				 c.SwaggerDoc( "v1", new Info
				 {
					 Title = "SmartHotel.Services.FacilityManagement",
					 Version = "v1",
				 } );

				 //Locate the XML file being generated by ASP.NET...
				 var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
				 var xmlPath = Path.Combine( AppContext.BaseDirectory, xmlFile );

				 //... and tell Swagger to use those XML comments.
				 c.IncludeXmlComments( xmlPath );

				 if ( !authFlow.UseAdalAuthFlow )
				 {
					 const string securityDefinitionName = "ApiKeyAuth";
					 c.AddSecurityDefinition( securityDefinitionName, new BasicAuthScheme() );
					 c.AddSecurityRequirement( new Dictionary<string, IEnumerable<string>>
					 {
						 { securityDefinitionName, Enumerable.Empty<string>() }
					 } );
					 c.OperationFilter<AddRequiredHeaderParameter>();
				 }
			 } );
			services.AddCors();
			services.AddHttpClient();
			services.AddScoped<ITopologyClient, TopologyClient>();
		}

		// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
		public void Configure( IApplicationBuilder app, IHostingEnvironment env )
		{
			if ( env.IsDevelopment() )
			{
				app.UseDeveloperExceptionPage();
			}
			else
			{
				app.UseHsts();
			}

			app.UseHttpsRedirection();

			app.UseCors( builder =>
			 {
				 builder
				 .AllowAnyOrigin()
				 .AllowAnyHeader()
				 .AllowAnyMethod();
			 } );


			app.UseSwagger();
			app.UseSwaggerUI( c =>
			 {
				 c.SwaggerEndpoint( "/swagger/v1/swagger.json", "Swagger Endpoint" );
			 } );

			app.UseAuthentication();

			app.UseMvc();

		}
	}
}
